#!/usr/bin/env python
# coding: utf-8

# In[1]:


"""二分类问题"""
"""生成类的神经网络模型"""

import numpy as np
from sklearn import datasets, linear_model
from sklearn.metrics import accuracy_score
import matplotlib.pyplot as plt


# 定义激活函数
def sigmoid(X):
    return 1.0/(1+np.exp(-X))


# 生成神经网络模型
class NN_Model:
    def __init__(self, nodes=None): 
        # 设定初始神经网络层次结构
        self.epsilon = 0.01                 # 学习率
        self.n_epoch = 1000                 # 迭代次数
        
        if not nodes:
            self.nodes = [2, 8, 2]          # 设置默认神经网络层数和节点数（输入到输出）
        else:
            self.nodes = nodes
    
    def init_wb(self):
        # 设定初始权重偏置
        W = []
        B = []
        
        n_layer = len(self.nodes)
        for i in range(n_layer-1):
            w = np.random.randn(self.nodes[i], self.nodes[i+1]) / np.sqrt(self.nodes[i])
            b = np.random.randn(1, self.nodes[i+1])
            
            W.append(w)
            B.append(b)
            
        self.W = W
        self.B = B
    
    # 正向计算
    def forward(self, X):
        # 前向节点间计算公式（神经网络前向通路结构）
        Z = []
        x0 = X
        for i in range(len(self.nodes)-1):
            z = sigmoid(np.dot(x0, self.W[i]) + self.B[i])
            x0 = z
            
            Z.append(z)
        
        self.Z = Z
        return Z[-1]
        
    # 反向传播
    def backpropagation(self, X, y, n_epoch=None, epsilon=None):
        if not n_epoch: n_epoch = self.n_epoch
        if not epsilon: epsilon = self.epsilon
        
        self.X = X
        self.Y = y
        
        for i in range(n_epoch):
            # 导入前向计算各节点输出
            self.forward(X)

            self.epoch = i
            self.evaluate()
            
            # 计算权重更新
            W = self.W
            B = self.B
            Z = self.Z
            
            D = []
            d0 = y
            n_layer = len(self.nodes)
            for j in range(n_layer-1, 0, -1):
                jj = j - 1
                z = self.Z[jj]
                
                if j == n_layer - 1:
                    d = z*(1-z)*(d0 - z)
                else:
                    d = z*(1-z)*np.dot(d0, W[j].T)
                    
                d0 = d
                D.insert(0, d)
            
            # 更新权重
            for j in range(n_layer-1, 0, -1):
                jj = j - 1
                
                if jj != 0:
                    W[jj] += epsilon * np.dot(Z[jj-1].T, D[jj])
                else:
                    W[jj] += epsilon * np.dot(X.T, D[jj])
                    
                B[jj] += epsilon * np.sum(D[jj], axis=0)
        
    def evaluate(self):
        # 定义评价函数
        z = self.Z[-1]
        
        # 输出损失、精确度
        L = np.sum((z - self.Y)**2)
            
        y_pred = np.argmax(z, axis=1)
        y_true = np.argmax(self.Y, axis=1)
        acc = accuracy_score(y_true, y_pred)
        
        if self.epoch % 100 == 0:
            print("L = %f, acc = %f" % (L, acc))


# In[2]:


"""导入月形数据图"""

# 生成示例数据
np.random.seed(0)
X, y = datasets.make_moons(200, noise=0.20) # 导入月形图数据

# 生成神经网络目标输出值
t = np.zeros((X.shape[0], 2))
t[np.where(y==0), 0] = 1
t[np.where(y==1), 1] = 1

# 示例数据可视化
plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Spectral)
plt.show()


# In[3]:


"""训练神经网络模型"""

nn = NN_Model([2, 8, 7, 2]) # 建立四层[2, 8, 7, 2]神经网络
nn.init_wb() # 初始化神经网络权重
nn.backpropagation(X, t, 5000) # 反向更新网络权重


# In[4]:


"""可视化显示分类结果"""

y_res  = nn.forward(X)
y_pred = np.argmax(y_res, axis=1)

# 分类结果可视化
plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Spectral)
plt.title("truth figure")
plt.show()

plt.scatter(X[:, 0], X[:, 1], c=y_pred, cmap=plt.cm.Spectral)
plt.title("predicted figure")
plt.show()


# In[ ]:





# In[ ]:




